package com.vipboot.platform.system.crud.service.rbac;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.vipboot.framework.core.constant.Const;
import com.vipboot.framework.core.exception.ServiceException;
import com.vipboot.framework.core.util.MapStructUtil;
import com.vipboot.framework.core.util.StringUtil;
import com.vipboot.framework.core.util.TreeBuildUtil;
import com.vipboot.framework.satoken.helper.LoginHelper;
import com.vipboot.platform.system.crud.dao.SysMenuDao;
import com.vipboot.platform.system.crud.dao.SysRoleMenuDao;
import com.vipboot.platform.system.domain.bo.SysMenuBo;
import com.vipboot.platform.system.domain.entity.SysMenu;
import com.vipboot.platform.system.domain.entity.SysRole;
import com.vipboot.platform.system.domain.entity.SysRoleMenu;
import com.vipboot.platform.system.domain.entity.SysUserRole;
import com.vipboot.platform.system.domain.vo.SysMenuVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;


@Slf4j
@RequiredArgsConstructor
@Service
public class SysMenuService {

    private final SysMenuDao sysMenuDao;
    private final SysRoleMenuDao sysRoleMenuDao;

    /**
     * 查询系统菜单列表
     */
    public List<SysMenuVo> list(SysMenuBo bo, Long userId) {
        // 管理员显示所有菜单信息
        if (LoginHelper.isSuperAdmin(userId)) {
            LambdaQueryWrapper<SysMenu> lqw = Wrappers.lambdaQuery();
            lqw.in(SysMenu::getMenuType, Const.MenuTypeDir, Const.MenuTypeMenu)
                    .eq(SysMenu::getStatus, Const.StatusNormal)
                    .orderByAsc(SysMenu::getParentId)
                    .orderByAsc(SysMenu::getSortNum);
            return sysMenuDao.listVo(lqw, SysMenuVo.class);
        }

         /*select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
            from sys_menu m
            left join sys_role_menu rm on m.menu_id = rm.menu_id
            left join sys_user_role ur on rm.role_id = ur.role_id
            left join sys_role ro on ur.role_id = ro.role_id
            where ur.user_id = #{params.userId}
            <if test="menuName != null and menuName != ''">
                    AND m.menu_name like concat('%', #{menuName}, '%')
            </if>
            <if test="visible != null and visible != ''">
                    AND m.visible = #{visible}
            </if>
            <if test="status != null and status != ''">
                    AND m.status = #{status}
            </if>
                order by m.parent_id, m.order_num*/

        MPJLambdaWrapper<SysMenu> queryWrapper = JoinWrappers.lambda();
        queryWrapper.select(SysMenu::getMenuId, SysMenu::getParentId, SysMenu::getMenuName, SysMenu::getPath,
                SysMenu::getComponent, SysMenu::getQueryParam, SysMenu::getVisible, SysMenu::getStatus,
                SysMenu::getPerms, SysMenu::getIsFrame, SysMenu::getIsCache, SysMenu::getMenuType, SysMenu::getIcon,
                SysMenu::getSortNum, SysMenu::getCreateTime).distinct();
        queryWrapper.leftJoin(SysRoleMenu.class, SysRoleMenu::getMenuId, SysMenu::getMenuId)
                .leftJoin(SysUserRole.class, SysUserRole::getRoleId, SysRoleMenu::getRoleId)
                .leftJoin(SysRole.class, SysRole::getRoleId, SysUserRole::getRoleId)
                .eq(SysUserRole::getUserId, userId)
                .eq(SysRole::getStatus, Const.StatusNormal)
                .eq(SysMenu::getStatus, Const.StatusNormal)
                .orderByAsc(SysMenu::getParentId)
                .orderByAsc(SysMenu::getSortNum);
        return sysMenuDao.selectJoinList(SysMenuVo.class, queryWrapper);
    }

    public boolean save(SysMenuBo bo) {
        if (this.checkExistMenuName(bo)) {
            throw new ServiceException("新增菜单'" + bo.getMenuName() + "'失败，菜单名称已存在");
        }
        if (ObjectUtil.equals(0, bo.getIsFrame()) && StringUtil.isNotHttp(bo.getPath())) {
            throw new ServiceException("新增菜单'" + bo.getMenuName() + "'失败，请填写http(s)://开头的路径");
        }
        SysMenu entity = MapStructUtil.convert(bo, SysMenu.class);
        return sysMenuDao.save(entity);
    }

    public boolean update(SysMenuBo bo) {
        if (this.checkExistMenuName(bo)) {
            throw new ServiceException("修改菜单'" + bo.getMenuName() + "'失败，菜单名称已存在");
        }
        if (ObjectUtil.equals(0, bo.getIsFrame()) && StringUtil.isNotHttp(bo.getPath())) {
            throw new ServiceException("修改菜单'" + bo.getMenuName() + "'失败，请填写http(s)://开头的路径");
        }
        if (ObjectUtil.equals(bo.getMenuId(), bo.getParentId())) {
            throw new ServiceException("修改菜单'" + bo.getMenuName() + "'失败，上级菜单不能选择自己");
        }
        SysMenu entity = MapStructUtil.convert(bo, SysMenu.class);
        return sysMenuDao.updateById(entity);
    }

    public boolean removeById(Long menuId) {
        if (this.hasChildByMenuId(menuId)) {
            throw new ServiceException("存在子菜单不支持删除");
        }
        if (this.checkMenuHasRoleByMenuId(menuId)) {
            throw new ServiceException("菜单已分配角色，不允许删除");
        }
        return sysMenuDao.removeById(menuId);
    }


    /**
     * 构建前端所需要树结构 (正常状态)
     */
    public List<Tree<Long>> tree(Long userId) {
        SysMenuBo bo = new SysMenuBo();
        bo.setStatus(Const.StatusNormal);

        List<SysMenuVo> list = this.list(bo, userId);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }

        return TreeBuildUtil.build(list, (menu, tree) ->
                tree.setId(menu.getMenuId())
                        .setParentId(menu.getParentId())
                        .setName(menu.getMenuName())
                        .setWeight(menu.getSortNum()));
    }

    /**
     * 校验菜单名称是否唯一
     */
    private boolean checkExistMenuName(SysMenuBo bo) {
        long menuId = ObjectUtil.isNull(bo.getMenuId()) ? -1L : bo.getMenuId();
        LambdaQueryWrapper<SysMenu> lqw = new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getMenuName, bo.getMenuName())
                .eq(SysMenu::getParentId, bo.getParentId())
                .ne(SysMenu::getMenuId, menuId);
        return sysMenuDao.exists(lqw);
    }

    /**
     * 是否存在菜单子节点
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    private boolean hasChildByMenuId(Long menuId) {
        LambdaQueryWrapper<SysMenu> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysMenu::getParentId, menuId);
        return sysMenuDao.exists(lqw);
    }

    /**
     * 菜单是否被角色分配
     */
    private boolean checkMenuHasRoleByMenuId(Long menuId) {
        LambdaQueryWrapper<SysRoleMenu> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysRoleMenu::getMenuId, menuId);
        return sysRoleMenuDao.exists(lqw);
    }

}
